class_name JumpAbility
extends GameplayAbility

var _jumped: bool = false

var _can_jump: bool = false

var _jump_cooldown: bool = false

#region 跳跃缓冲
var _jump_buffer_time: float = 0.15

var _jump_buffer_timer: float = 0
#endregion

#region 跳跃保持
var _small_jump: bool = false

var _jump_keep_time: float = 0.3

var _jump_keep_timer: float = 0

var _jump_keep_start_once_triggered: bool = false

var _jump_keep_end_once_triggered: bool = false
#endregion

#region 土狼跳跃
var _coyote_once_triggered: bool = false

var _coyote_time: float = 0.2

var _coyote_timer: float = 0
#endregion

@onready var player: Player = $"../../"


func _on_setup() -> void:
	player.player_input_handler.jump_checker.released.connect(_on_jump_action_released)


func _try_apply() -> bool:
	if not _jumped and \
	   # 处理小跳、跳跃缓冲、跳跃保持、土狼跳跃
	   _can_jump and \
	   # 如果长按跳跃键，则只触发一次跳跃
	   not _jump_cooldown and \
	   # 如果处于 Wall.Ledge 状态，则不可以跳跃
	   # 如果处于 Wall.Ledge.Grab 状态，则等待状态机转换为 Wall.Ledge.Climb 状态
	   # 如果处于 Wall.Ledge.Climb 状态，则等待状态机转换为其他的状态
	   not player.player_tag_manager.has_tag(player.player_tag_manager.State_Wall_Ledge):
		_jumped = true
		_jump_cooldown = true
		return true
	return false


func _apply() -> void:
	# 处理墙跳
	if player.player_tag_manager.has_tag(player.player_tag_manager.State_Wall):
		player.flip()
		# 将玩家移开墙面 3 个像素单位，不然玩家碰撞箱会卡着墙面从而卡掉竖直方向的跳跃速度
		player.global_position.x += player.face_direction * 3
		player.velocity.x = player.player_stats.jump_speed * player.face_direction
	player.velocity.y = -player.player_stats.jump_speed


func _on_update(delta: float) -> void:
	var is_ground_state: bool = player.player_tag_manager.has_tag(player.player_tag_manager.State_Ground)
	var is_wall_state: bool = player.player_tag_manager.has_tag(player.player_tag_manager.State_Wall)
	
	if is_ground_state:
		_jumped = false
		_can_jump = true
		_small_jump = false
		_coyote_once_triggered = false
		_jump_keep_start_once_triggered = false
		_jump_keep_end_once_triggered = false
	elif is_wall_state:
		_jumped = false
		_can_jump = true
	else:
		_can_jump = false

	#region 跳跃缓冲
	# 如果使用 not is_ground_state 来设置跳跃缓冲，会有误差
	# 即便是小跳（按下跳跃立即释放），not is_ground_state and player_input_handler.jump_input 也会在几帧内为 true，即小跳也会触发跳跃缓冲
	# 所以使用 player.player_tag_manager.has_tag(player.player_tag_manager.State_Air_Down) 来设置跳跃缓冲
	if player.player_tag_manager.has_tag(player.player_tag_manager.State_Air_Down):
		if player.player_input_handler.jump_input:
			# 当不在地面时，如果按下跳跃键，则设置跳跃缓冲计时器
			_jump_buffer_timer = _jump_buffer_time

		# 更新跳跃缓冲计时器
		_jump_buffer_timer -= delta

	# 如果跳跃缓冲计时器大于0，则可以跳跃
	if is_ground_state and _jump_buffer_timer > 0:
		_jump_buffer_timer = 0
		try_apply()
	#endregion

	#region 土狼跳跃
	# 当没有跳跃过，且不在地面，则处理土狼跳跃（排除墙跳）
	if not _jumped and not is_ground_state and not is_wall_state:
		# 设置土狼跳跃计时器
		if not _coyote_once_triggered:
			_coyote_once_triggered = true
			_coyote_timer = _coyote_time
		# 土狼跳跃计时器大于0，则可以跳跃
		else:
			_can_jump = _coyote_timer > 0

		# 更新土狼跳跃计时器
		_coyote_timer -= delta
	#endregion
	
	#region 跳跃保持
	# 当跳跃后，如果松开跳跃键，则认为是小跳，小跳不能进入跳跃保持
	if _jumped and not player.player_input_handler.jump_input:
		_small_jump = true
	
	# 当跳跃后，且判断不是小跳，如果按住跳跃键，则认为是跳跃保持
	if _jumped and not _small_jump and player.player_input_handler.jump_keep_input and not _jump_keep_end_once_triggered:
		# 跳跃保持会持续增加跳跃速度
		player.velocity.y -= player.player_stats.jump_keep_speed_agument * delta

		# 跳跃保持开始时，设置跳跃保持计时器
		if not _jump_keep_start_once_triggered:
			_jump_keep_start_once_triggered = true
			_jump_keep_timer = _jump_keep_time

		# 跳跃保持计时器小于0时，认为跳跃保持结束
		if _jump_keep_timer < 0:
			_jump_keep_end_once_triggered = true

		# 更新跳跃保持计时器
		_jump_keep_timer -= delta
	#endregion


func _on_jump_action_released(_current_time: float) -> void:
	_jump_cooldown = false
